﻿// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Runtime.InteropServices;
using System.Text;

namespace Windows.Win32;

internal static partial class PInvoke
{
    /// <inheritdoc cref="CreateWindowEx(WINDOW_EX_STYLE, string, string, WINDOW_STYLE, int, int, int, int, HWND, HMENU, HINSTANCE, void*)"/>
    public static unsafe HWND CreateWindowEx(
        WINDOW_EX_STYLE dwExStyle,
        string? lpClassName,
        string? lpWindowName,
        WINDOW_STYLE dwStyle,
        int X,
        int Y,
        int nWidth,
        int nHeight,
        HWND hWndParent,
        HMENU hMenu,
        HINSTANCE hInstance,
        object? lpParam)
    {
        fixed (char* cn = lpClassName)
        fixed (char* wn = lpWindowName)
        {
            if (lpParam is null)
            {
                // No need to marshal.
                return CreateWindowEx(dwExStyle, cn, wn, dwStyle, X, Y, nWidth, nHeight, hWndParent, hMenu, hInstance, null);
            }

            // Trying to replicate [MarshalAs(UnmanagedType.AsAny)] here. Note that the runtime has an AsAnyMarshaller
            // in src/coreclr/System.Private.CoreLib/src/System/StubHelpers.cs, but it isn't publicly available.
            // The implmentations for these Marshal methods can be found in marshalnative.cpp.

            // The only case we are not handling is non-blittable arrays. This is a breaking change, but it
            // is very unlikely anyone will have been doing this. If we find this to be a problem we can revist
            // and potentially port the code from StubHelpers.

            if (lpParam is StringBuilder builder)
            {
                lpParam = builder.ToString();
            }

            if (lpParam is string stringValue)
            {
                fixed (char* sv = stringValue)
                {
                    return CreateWindowEx(dwExStyle, cn, wn, dwStyle, X, Y, nWidth, nHeight, hWndParent, hMenu, hInstance, sv);
                }
            }

            int size = Marshal.SizeOf(lpParam);
            nint native = Marshal.AllocCoTaskMem(size);
            try
            {
                // This will emit IL to create a marshaller for the given object if it isn't blittable.
                Marshal.StructureToPtr(lpParam, native, fDeleteOld: false);
                return CreateWindowEx(dwExStyle, cn, wn, dwStyle, X, Y, nWidth, nHeight, hWndParent, hMenu, hInstance, (void*)native);
            }
            finally
            {
                if (native != 0)
                {
                    Marshal.DestroyStructure(native, lpParam.GetType());
                    Marshal.FreeCoTaskMem(native);
                }
            }
        }
    }
}
